﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tracy.Plugins.DownloadAdapters.ServiceDefinition;
using System.Collections.Specialized;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;
using System.ServiceModel;
using System.Diagnostics;
using Tracy.DataModel;
using System.IO;
using System.Net;

namespace Tracy.Plugins.DownloadAdapters.Model
{
    class UTorrentWebUIClient
    {
        private const string UTORRENT_WEBUI_URL = "http://localhost:{0}/gui";
        IUTorrentWebAPI _client;
        StringDictionary _settings;
        string _token;
        private void LoadSettings()
        {
            if (Connected)
            {
                //Load Settings
                _settings = new StringDictionary();
                foreach (UTorrentSettings.Setting s in _client.GetSettings(_token).Settings)
                {
                    _settings.Add(s.Name, s.Value);
                }
            }
        }

        public bool Connected
        {
            get { return Ready && (!String.IsNullOrEmpty(_token)); }
        }

        public bool Ready
        {
            get { return _client != null && Process.GetProcessesByName("uTorrent").Length > 0; }
        }

        public void Reset()
        {
            _client = null;
        }

        public void LoadConfig(UTorrentConfiguration _currentConfig)
        {
            CreateClient(_currentConfig);
            RefreshToken();
            LoadSettings();
        }

        private void CreateClient(UTorrentConfiguration config)
        {
            //Create uTorrent Web API client
            CustomBinding uTorrentCustomBinding = new CustomBinding(
                new WebMessageEncodingBindingElement() { ContentTypeMapper = new JsonContentTypeMapper() },
                new HttpTransportBindingElement() { ManualAddressing = true, AuthenticationScheme = System.Net.AuthenticationSchemes.Basic, Realm = "uTorrent", AllowCookies = true, MaxReceivedMessageSize = 2097152 }
                );

            WebChannelFactory<IUTorrentWebAPI> factory = new WebChannelFactory<IUTorrentWebAPI>();
            factory.Endpoint.Address = new EndpointAddress(string.Format(UTORRENT_WEBUI_URL, config.Port));
            factory.Endpoint.Binding = uTorrentCustomBinding;
            factory.Credentials.UserName.UserName = config.UserName;
            factory.Credentials.UserName.Password = config.Password;
            _client = factory.CreateChannel();
        }

        public void RefreshToken()
        {
            if (Ready)
            {
                //Store Security Token
                _token = _client.GetToken().Token;
            }
        }

        public void SetDefaultPath(string defaultPath)
        {
            //Set default folder
            if (_settings["dir_completed_download"] != defaultPath)
            {
                _client.SetSetting("dir_completed_download", defaultPath, _token);
                _settings["dir_completed_download"] = defaultPath;
            }
            if (_settings["dir_active_download_flag"] != "true")
            {
                _client.SetSetting("dir_active_download_flag", "1", _token);
                _settings["dir_active_download_flag"] = "true";
            }
            if (_settings["append_incomplete"] != "true")
            {
                _client.SetSetting("append_incomplete", "1", _token);
                _settings["append_incomplete"] = "true";
            }
            if (_settings["dir_active_download"] != defaultPath)
            {
                _client.SetSetting("dir_active_download", defaultPath, _token);
                _settings["dir_active_download"] = defaultPath;
            }
        }

        public UTorrentWebUIClient()
        {
            
        }

        public void Download(Resource res)
        {
            if (Connected)
            {
                
                if (res.Attributes.ContainsKey("local-torrent-file") && res.Attributes.ContainsKey("torrent-name"))
                {
                    string file = res.Attributes["local-torrent-file"];
                    string name = res.Attributes["torrent-name"];
                    if (File.Exists(file))
                    {
                        //Upload torrent file to uTorrent
                        UTorrentResponse result = null;
                        new TokenRetryHelper(this).run(() => {
                            result = _client.AddFile(new FileStream(file, FileMode.Open, FileAccess.Read), _token);
                        });
                        if (result == null) throw new InvalidOperationException("Failed to add file to uTorrent: empty response.");
                        if (!string.IsNullOrEmpty(result.Error)) throw new InvalidOperationException("Failed to add file to uTorrent: " + result.Error + ".");
                        
                        //Confirm the task is added
                        bool assigned = false;
                        var items = _client.GetItemList(_token);
                        for (int i = items.Torrents.Count - 1; i >= 0; i--)
                        {
                            if (String.Equals(items.Torrents[i].Name, name, StringComparison.OrdinalIgnoreCase))
                            {
                                assigned = true;
                                break;
                            }
                        }
                        if (!assigned) throw new InvalidOperationException("Failed to add file to uTorrent: Torrent added but not found in list.");
                    }
                }
                else
                {
                    //Add torrent url to uTorrent
                    _client.AddUrl(res.Url, _token);
                }
            }
        }

        //Refresh Token if connection failed due to invalid token
        private class TokenRetryHelper{
            bool retry = false;
            UTorrentWebUIClient caller;

            public TokenRetryHelper(UTorrentWebUIClient caller)
            {
                this.caller = caller;
            }

            public void run(Action action)
            {
                try
                {
                    action.Invoke();
                }
                catch (ProtocolException pe)
                {
                    if (String.Equals(pe.Message, "The remote server returned an unexpected response: (400) ERROR.", StringComparison.OrdinalIgnoreCase))
                    {
                        if (!retry)
                        {
                            retry = true;
                            caller.RefreshToken();
                            run(action);
                        }
                    }
                    else
                    {
                        throw;
                    }
                }
            }
        }
    }
}
